/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://code.google.com/p/dylab/

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_SignalRouter_h__
#define __dylab_SignalRouter_h__

//===========================================================================================
#include <dylab/utils/containers/HashContainer.h>
#include <dylab/utils/exceptions/EDoesNotExists.h>

//===========================================================================================
namespace dylab {
    
    /** 
     * 
     */    
    template <
        class _ClassT,
        typename _SignalT,
        typename _ActionT = void(_ClassT::*)()
    >
    class SignalRouter     
    {
    public:
        // action type
        typedef typename _ActionT ActionT;

        // signal -> action map
        class ActionMap
            : public HashContainer<_SignalT, _ActionT> 
        {
        };
        //-----------------------------------------------------------------------------

    private:
        // action map
        ActionMap mActionMap;

    public:

        /**
         *
         */
        SignalRouter()
                { }

        /**
         *
         */
        virtual ~SignalRouter()
                { }

        /**
         *
         */
        inline ActionMap * getActionMap()
                { return & mActionMap; }

        /**
         *
         */
        inline const ActionMap * getActionMap() const
                { return & mActionMap; }

        /**
         *
         */
        inline void bindAction(const _SignalT & _signal, _ActionT _action)
                { mActionMap.insert(_signal, _action); }

        /**
         *
         */
        inline void unbindAction(const _SignalT & _signal)
                { mActionMap.remove(_signal); }

        /**
         *
         */
        inline bool trySignal(const _SignalT & _signal, _ClassT * _obj)
                { 
                    DYLAB_ASSERT(_obj != NULL);

                    _ActionT action;
                    if (mActionMap.find(_signal, &action))
                    {
                        (_obj->*action)();
                        return true; 
                    }
                    else
                    {
                        return false;
                    }
                }                

        /**
         *
         */
        template <typename _ArgT>
        inline bool trySignal(const _SignalT & _signal, _ClassT * _obj, _ArgT _arg)
                { 
                    DYLAB_ASSERT(_obj != NULL);

                    _ActionT action;
                    if (mActionMap.find(_signal, &action))
                    {
                        (_obj->*action)(_arg);
                        return true; 
                    }
                    else
                    {
                        return false;
                    }
                }                

        /**
         *
         */
        template <typename _Arg1T, typename _Arg2T>
        inline bool trySignal(const _SignalT & _signal, _ClassT * _obj, _Arg1T _arg1, _Arg2T _arg2)
                { 
                    DYLAB_ASSERT(_obj != NULL);

                    _ActionT action;
                    if (mActionMap.find(_signal, &action))
                    {
                        (_obj->*action)(arg1, arg2);
                        return true; 
                    }
                    else
                    {
                        return false;
                    }
                }                

        /**
         *
         */
        inline void routeSignal(const _SignalT & _signal, _ClassT * _obj)
                { 
                    DYLAB_ASSERT(_obj != NULL);
                    if (!trySignal(_signal, _obj))
                        throw EDoesNotExists("Signal not bound '%1%'.", _signal);
                }                

        /**
         *
         */
        template <typename _ArgT>
        inline void routeSignal(const _SignalT & _signal, _ClassT * _obj, _ArgT _arg)
                { 
                    DYLAB_ASSERT(_obj != NULL);
                    if (!trySignal(_signal, _obj, _arg))
                        throw EDoesNotExists("Signal not bound '%1%'.", _signal);
                }                

        /**
         *
         */
        template <typename _Arg1T, typename _Arg2T>
        inline void routeSignal(const _SignalT & _signal, _ClassT * _obj, _Arg1T _arg1, _Arg2T _arg2)
                { 
                    DYLAB_ASSERT(_obj != NULL);
                    if (!trySignal(_signal, _obj, _arg1, _arg2))
                        throw EDoesNotExists("Signal not bound '%1%'.", _signal);
                }                
    };
}
//===========================================================================================
#endif // __dylab_SignalRouter_h__
